/*
 * Copyright (C) 2010-2011 Comcast Cable.  All Rights Reserved.
 */
package com.hercules.android.receiver.handlers.commands.call.animation;

import android.opengl.Matrix;
import android.util.Log;

import com.hercules.android.receiver.model.objects.XREView;

/**
 * Helper class for animations.
 * <p>
 * AndroidAssembly2 this class was added in Android Assembly 2.
 *
 * @author 7realm
 * @version 1.0
 */
public final class AnimationUtil {
    /** Quantity of dimensions in four-dimension space. */
    private static final int FOUR_DIM = 4;

    /**
     * Private constructor for utility class to prevent initialization.
     */
    private AnimationUtil() {
        // empty
    }

    /**
     * Get the scale values from given transformation matrix.
     *
     * @param matrix the transformation matrix
     * @return the scale values stored in a Vector3d object
     */
    static Vector3d getScale(float[] matrix) {
        return new Vector3d(Matrix.length(matrix[0], matrix[1], matrix[2]), Matrix.length(matrix[4], matrix[5],
            matrix[6]), Matrix.length(matrix[8], matrix[9], matrix[10]));
    }

    /**
     * Get the rotation values from given transformation matrix.
     *
     * @param matrix the transformation matrix
     * @return the rotation values stored in a Vector3d object
     */
    static Vector3d getRotation(float[] matrix) {
        // get scale value
        Vector3d scale = getScale(matrix);

        // use a Vector3d array to store the 3x3 matrix that contains the scaling and rotation
        Vector3d[] columns =
            new Vector3d[]{new Vector3d(matrix[0], matrix[1], matrix[2]),
                new Vector3d(matrix[4], matrix[5], matrix[6]), new Vector3d(matrix[8], matrix[9], matrix[10])};

        // update columns, that are required in future
        if (scale.getX() != 0) {
            columns[0].setZ(columns[0].getZ() / scale.getX());
        }
        if (scale.getY() != 0) {
            columns[1].setZ(columns[1].getZ() / scale.getY());
        }
        if (scale.getZ() != 0) {
            columns[2].setX(columns[2].getX() / scale.getZ());
            columns[2].setY(columns[2].getY() / scale.getZ());
            columns[2].setZ(columns[2].getZ() / scale.getZ());
        }

        // create a rotation matrix, we init only fields, that will be used in future
        float[][] rotationMatrix = new float[FOUR_DIM][FOUR_DIM];
        rotationMatrix[0][0] = columns[0].getX();
        rotationMatrix[1][0] = columns[0].getY();
        rotationMatrix[2][0] = columns[0].getZ();
        rotationMatrix[2][1] = columns[1].getZ();
        rotationMatrix[2][2] = columns[2].getZ();

        // calculate the rotation (in radians)
        return new Vector3d((float) -Math.asin(rotationMatrix[2][0]), (float) Math.atan2(rotationMatrix[2][1],
            rotationMatrix[2][2]), (float) Math.atan2(rotationMatrix[1][0], rotationMatrix[0][0]));
    }

    /**
     * Get the translation values from given transformation matrix.
     *
     * @param matrix the transformation matrix
     * @return the translation values stored in a Vector3d object
     */
    static Vector3d getTranslation(float[] matrix) {
        return new Vector3d(matrix[12], matrix[13], matrix[14]);
    }

    /**
     * Convert the matrix data stored in two dimensional array to one dimensional array. The matrix data stored in one
     * dimensional array is in column-major order.
     * <p>
     * Note that the android.opengl.Matrix utility class and the utility methods (getScale(), getRotation() and
     * getTranslation()) defined in current class require matrix data to be stored in one dimensional array, while the
     * XREView#matrix stores data in two dimensional array, so a conversion is need.
     *
     * @param matrix the matrix data stored in two dimensional array
     * @return the one dimensional array containing matrix data in column-major order
     */
    static float[] toOneDimensionalArray(double[][] matrix) {
        float[] array = new float[FOUR_DIM * FOUR_DIM];
        for (int i = 0; i < FOUR_DIM; i++) {
            for (int j = 0; j < FOUR_DIM; j++) {
                array[i * FOUR_DIM + j] = (float) matrix[j][i];
            }
        }
        return array;
    }

    /**
     * Convert the matrix data stored in one dimensional array to two dimensional array. The matrix data stored in one
     * dimensional array is in column-major order.
     * <p>
     * Note that the android.opengl.Matrix utility class and the utility methods (getScale(), getRotation() and
     * getTranslation()) defined in current class require matrix data to be stored in one dimensional array, while the
     * XREView#matrix stores data in two dimensional array, so a conversion is need.
     *
     * @param matrix the matrix data stored in one dimensional array in column-major order
     * @return the two dimensional array containing matrix data
     */
    static double[][] toTwoDimensionalArray(float[] matrix) {
        double[][] array = new double[FOUR_DIM][FOUR_DIM];
        for (int i = 0; i < FOUR_DIM; i++) {
            for (int j = 0; j < FOUR_DIM; j++) {
                array[j][i] = matrix[i * FOUR_DIM + j];
            }
        }
        return array;
    }


    /**
     * Gets current scale of the XRE view matrix.
     *
     * @param xreView the corresponding XRE view
     * @return current scale
     */
    static Vector3d getCurrentScale(XREView xreView) {
        // create a copy of matrix
        float[] matrix = toOneDimensionalArray(xreView.getMatrix());

        // calculate current rotation
        Vector3d rotation = getRotation(matrix);

        // set rotation value to zero
        Matrix.rotateM(matrix, 0, -rotation.getZ(), 0, 0, 1);

        // calculate current scale
        Vector3d scale = getScale(matrix);

        Log.d("anim", "Scale " + scale);
        return scale;
    }

}
